{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a target=\"_blank\" href=\"https://colab.research.google.com/github/opensim-org/opensim-core/blob/main/Bindings/Python/tutorials/Tutorial%204%20-%20Musculoskeletal%20models%2C%20Motion%20Files%20and%20MuscleAnalysis.ipynb\">\n",
    "  <img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/>\n",
    "</a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vOsTxESLc7wl"
   },
   "source": [
    "# 4. Musculoskeletal Models, Motion Files, and `MuscleAnalysis`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "SwX6QwGU0Jwj"
   },
   "source": [
    "## 4.1. Objectives"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4p_3qlB60Np3"
   },
   "source": [
    "**Introduction**\n",
    "\n",
    "Musculoskeletal models enable us to study neuromuscular coordination, analyze athletic performance, and estimate musculoskeletal loads [[1]](https://mitpress.mit.edu/9780262044202/biomechanics-of-movement/). In [OpenSim](https://opensim.stanford.edu/) [[2]](https://doi.org/10.1109/TBME.2007.901024), a musculoskeletal model consists of rigid body segments connected by joints. Muscles span these joints and generate forces and motion. Once a musculoskeletal model is created, OpenSim enables users to create custom studies, including investigating the effects of musculoskeletal geometry, joint kinematics, and muscle-tendon properties on the forces and joint moments that the muscles can produce. With OpenSim, our goal is to provide a framework that allows the biomechanics community to create, share, and extend a library of models and dynamic simulation tools that can be used to study and quantify human and animal movement.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4SuUmksVUl5b"
   },
   "source": [
    "**Purpose**\n",
    "\n",
    "The purpose of this tutorial is to introduce users to the OpenSim `conda` package for musculoskeletal modeling by investigating how muscle-tendon lengths and moment arms depend on limb configuration. In this tutorial, you will:\n",
    "\n",
    "*   Become familiar with OpenSim's API.\n",
    "*   Explore a lower-extremity musculoskeletal model.\n",
    "*   Work with motion files.\n",
    "*   Use the `MuscleAnalysis` tool.\n",
    "*   Use OpenSim to analyze an important clinical problem."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-Ko_AC3hUiqk"
   },
   "source": [
    "**Format**\n",
    "\n",
    "Each section of the tutorial guides you through certain tools within OpenSim and asks you to answer a few questions. The questions can be answered based on information from OpenSim and basic knowledge of the human musculoskeletal system. As you complete each section of the tutorial, feel free to explore OpenSim and the lower extremity model further on your own."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "D3abtOlWUewq"
   },
   "source": [
    "\n",
    "**Resources**\n",
    "\n",
    "All of the files necessary to complete this tutorial are available in the following Google Drive folder: https://drive.google.com/drive/folders/1oaSigQup8DNEpXX5_Z6Edk7PMKBXvFOq?usp=sharing\n",
    "\n",
    "While you complete the tutorial, you will find cells that automatically download the required files.\n",
    "\n",
    "To obtain a link to one of the files in Google Drive, right-click over it and click on **Get Link**.\n",
    "\n",
    "**Local Installation**\n",
    "\n",
    "You may also run this notebook using a locally installed OpenSim `conda` package. Visit [the OpenSim python scripting Confluence page](https://opensimconfluence.atlassian.net/wiki/spaces/OpenSim/pages/53085346/Scripting+in+Python) for more information on how to install OpenSim in a local Python environment. You will also need to download the resources from Google Drive to the same directory as this notebook. Then, you may skip to Section 4.3 below."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ADllcxbsjTr8"
   },
   "source": [
    "## 4.2. Set up `conda` and OpenSim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mmmTuEI4HQJG"
   },
   "source": [
    "First, set up the environment by executing the following cell (see [Tutorial 1: Introduction to OpenSim](https://colab.research.google.com/github/opensim-org/opensim-core/blob/main/Bindings/Python/tutorials/Tutorial%201%20-%20Introduction%20to%20OpenSim.ipynb) for more information)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "0w9LYQXx5TO4"
   },
   "outputs": [],
   "source": [
    "!pip install -q condacolab\n",
    "import condacolab\n",
    "condacolab.install()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vt5loGbdfKb8"
   },
   "source": [
    "Don't worry if after executing the previous cell you get an error saying that your session has failed. This happens because `condacolab` needs to restart the session for the changes to take effect. Therefore, you have to execute the previous cell before executing any other cell.\n",
    "\n",
    "Now, execute the following cell to install the OpenSim `conda` package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "vWf9ObZJ5TO5"
   },
   "outputs": [],
   "source": [
    "!conda install opensim-org::opensim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4gtTNdaO8wOI"
   },
   "source": [
    "## 4.3. Downloading and exploring a lower-extremity model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "SJ2b6Dx10dD5"
   },
   "source": [
    "In this section, you will download a model of the lower extremity [[3]](https://doi.org/10.1109/10.102791), and then load it into OpenSim. The model represents an adult subject with an approximate height of 1.8 m and an approximate mass of 75 kg. The model consists of 13 rigid body segments and includes the lines of action of 92 muscles (43 per leg and 6 at the torso)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TIQp3TtGYMyQ"
   },
   "source": [
    "First, let's download the model file (`gait2392.osim`). The file is stored in a Google Drive folder (See **Resources** in the Objectives section)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "LjMgzeHLYa2W"
   },
   "outputs": [],
   "source": [
    "!gdown \"1I7iCmCgrOj8mq8TV1n-r6JA1pFK6Q_z-&confirm=t\" # gait2392.osim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_F6Hrnjba7vL"
   },
   "source": [
    "Now, let's import OpenSim, load the model, and print some metadata."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "fmGIZ0Hl8z_G"
   },
   "outputs": [],
   "source": [
    "import opensim as osim\n",
    "\n",
    "# Load the model.\n",
    "gait2392 = osim.Model('gait2392.osim')\n",
    "\n",
    "# Print metadata.\n",
    "print(\"Name of the model:\", gait2392.getName())\n",
    "print(\"Author:\", gait2392.get_credits())\n",
    "print(\"Publications:\", gait2392.get_publications())\n",
    "print(\"Length Unit:\", gait2392.get_length_units())\n",
    "print(\"Force Unit:\", gait2392.get_force_units())\n",
    "print(\"Gravity:\", gait2392.get_gravity())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "hW93EJzR9dFx"
   },
   "source": [
    "Let's explore the coordinates of the model. We know the model has 23 coordinates from the name of the model file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "dj2PFUrD9gbF"
   },
   "outputs": [],
   "source": [
    "# Print the number of coordinates.\n",
    "print(\"Num Coordinates:\", gait2392.getNumCoordinates())\n",
    "print()\n",
    "\n",
    "# For each coordinate, print some information, such as its name or motion type.\n",
    "for coordinate in gait2392.getCoordinateSet():\n",
    "    print(\"  Coordinate Name:\", coordinate.getName())\n",
    "    print(\"  Coordinate Absolute Path:\", coordinate.getAbsolutePathString())\n",
    "  \n",
    "    # Motion type is an enumerate (0:Undefined, 1:Rotational, 2:Translational, 3:Coupled).\n",
    "    motion_type = coordinate.getMotionType()\n",
    "    motion_type_string = \"\"\n",
    "    if motion_type == 0:\n",
    "        motion_type_string = \"Undefined\"\n",
    "    elif motion_type == 1:\n",
    "        motion_type_string = \"Rotational\"\n",
    "    elif motion_type == 2:\n",
    "        motion_type_string = \"Translational\"\n",
    "    elif motion_type == 3:\n",
    "        motion_type_string = \"Coupled\"\n",
    "    print(\"  Coordinate Motion Type:\", motion_type_string)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "v_xgSNv9Yz2m"
   },
   "source": [
    "Let's explore some of the model's joint properties."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "2laouerdY59s"
   },
   "outputs": [],
   "source": [
    "# Print number of joints.\n",
    "print(\"Num Joints:\", gait2392.getNumJoints())\n",
    "print()\n",
    "\n",
    "# For each joint, print some information, such as its name or components.\n",
    "for joint in gait2392.getJointSet():\n",
    "    print(\"Joint Name:\", joint.getName())\n",
    "    print(\"Joint Absolute Path:\", joint.getAbsolutePathString())\n",
    "    print(\"Components:\")\n",
    "    for component in joint.getComponentsList():\n",
    "        print(\"  Component Name:\", component.getName())\n",
    "        print(\"  Component Absolute Path:\", component.getAbsolutePathString())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "30rBKV7S3hzt"
   },
   "source": [
    "And finally, let's explore the muscles in the model. We know the model has 92 muscles from the name of the model file.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Etbh4aon3hzt"
   },
   "outputs": [],
   "source": [
    "# Print the number of muscles.\n",
    "print(\"Num Muscles:\", gait2392.getMuscles().getSize())\n",
    "print()\n",
    "\n",
    "for muscle in gait2392.getMuscles():\n",
    "    print(\"Muscle Name:\", muscle.getName())\n",
    "    print(\"Muscle Absolute Path:\", muscle.getAbsolutePathString())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "D7_wfvV55skF"
   },
   "source": [
    "## 4.4. Working with motion files"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "FqIgcdg43Qe7"
   },
   "source": [
    "In this section, you will load information from a motion file. A motion file (`.mot`) is a tab separated values (tsv) file containing a header with information about the file (e.g., if the values are in degrees, number of columns, etc.), and a set of columns containing how different values in the model evolve over time (e.g., pelvis rotation, pelvis tilt, hip flexion).\n",
    "\n",
    "Since the motion file already contains the values resulting from a simulation, you will not need to simulate the model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "55eetMYEn8iK"
   },
   "source": [
    "First, let's download the motion file (`normal_gait.mot`). The file is stored in a Google Drive folder (See **Resources** in the Objectives section)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "M9srcF3YadZZ"
   },
   "outputs": [],
   "source": [
    "!gdown \"1bZq91PPNOHLYz6wIf8l0Zz4igAp54mtu&confirm=t\" # normal_gait.mot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "kFQ-3EmOn-zc"
   },
   "source": [
    "Load the file into a `TimeSeriesTable`. Print the column labels to check the file has been processed properly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "CWy88RN8hIMg"
   },
   "outputs": [],
   "source": [
    "# Use the TableProcessor to read the motion file.\n",
    "tableTime = osim.TimeSeriesTable('normal_gait.mot')\n",
    "\n",
    "# Print the column labels.\n",
    "print(tableTime.getColumnLabels())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1OrWnq7ZoEyV"
   },
   "source": [
    "In this example, we will plot the knee angle and hip adduction between both legs. Run the following cell to extract this information from the motion file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Cl6bsTwxoHfj"
   },
   "outputs": [],
   "source": [
    "# Get columns we want to analyze, and the time column (independent column).\n",
    "y_knee_angle_r = tableTime.getDependentColumn('knee_angle_r')\n",
    "y_knee_angle_l = tableTime.getDependentColumn('knee_angle_l')\n",
    "y_hip_adduction_r = tableTime.getDependentColumn('hip_adduction_r')\n",
    "y_hip_adduction_l = tableTime.getDependentColumn('hip_adduction_l')\n",
    "x_time = tableTime.getIndependentColumn()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "92S8pO4mdUc0"
   },
   "source": [
    "We can use Matplotlib to plot the information."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "XJt3WB8grQW6"
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# Create six subplots, with 2 rows and 3 columns.\n",
    "fig, axs = plt.subplots(2, 1, figsize=(10, 10))\n",
    "fig.suptitle('Normal Gait')\n",
    "\n",
    "# Plot the knee angles on the first subplot.\n",
    "axs[0].plot(x_time, y_knee_angle_r.to_numpy(), label='knee_angle_r')\n",
    "axs[0].plot(x_time, y_knee_angle_l.to_numpy(), label='knee_angle_l')\n",
    "axs[0].set_title('Knee angle')\n",
    "axs[0].set_xlabel('Time')\n",
    "axs[0].set_ylabel('Knee angle')\n",
    "axs[0].grid()\n",
    "axs[0].legend()\n",
    "\n",
    "# Plot the hip adductions on the second subplot.\n",
    "axs[1].plot(x_time, y_hip_adduction_r.to_numpy(), label='hip_adduction_r')\n",
    "axs[1].plot(x_time, y_hip_adduction_l.to_numpy(), label='hip_adduction_l')\n",
    "axs[1].set_title('Hip adduction')\n",
    "axs[1].set_xlabel('Time')\n",
    "axs[1].set_ylabel('Hip Adduction')\n",
    "axs[1].grid()\n",
    "axs[1].legend()\n",
    "\n",
    "# Set the spacing between subplots\n",
    "plt.subplots_adjust(wspace=0.5, hspace=0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iwjKjz4trPYr"
   },
   "source": [
    "## 4.5. Using `MuscleAnalysis` to assess hamstring length during crouch gait"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Na1olOGW460V"
   },
   "source": [
    "In this final section of the tutorial, you will use OpenSim to investigate a possible cause of crouch gait, one of the most common walking abnormalities among individuals with cerebral palsy. It is characterized by excessive flexion of the knee during stance phase, which is often accompanied by exaggerated flexion and internal rotation of the hip. One hypothesized cause of crouch gait is short hamstrings, and orthopedic surgeons will sometimes lengthen the hamstrings of such patients in an attempt to improve their posture and gait. However, other causes of excessive knee flexion are possible (e.g., weak ankle plantar flexors), and lengthening the hamstrings can compromise these muscles' force-generation capabilities [[4]](https://doi.org/10.1016/j.gaitpost.2005.03.003). How can a surgeon determine whether a hamstring lengthening procedure is warranted? \n",
    "\n",
    "One possible way to judge whether a patient's hamstrings are shorter than \"normal\" is to develop a musculoskeletal model and compare the length of the hamstrings during the patient's crouch gait cycle to the length of the hamstrings during a normal gait cycle. Suppose that an orthopaedic surgeon has brought you some kinematic data for a patient who walks with a crouch gait. The surgeon is contemplating whether to operate and wants your opinion.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "l393sw739Guj"
   },
   "source": [
    "### 4.5.1. Range of Motion"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "q4v56Rx-ra29"
   },
   "source": [
    "First, let's download the crouch gait motion file (`crouch_gait.mot`).The file is stored in a Google Drive folder (See **Resources** in the Objectives section). We already downloaded the normal gait motion file in the previous section."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "S7rYuVrvbtNQ"
   },
   "outputs": [],
   "source": [
    "!gdown \"11XGpKPxX4Y6SO4xjPYUQz9gZdjvKyuLH&confirm=t\" # crouch_gait.mot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1w4Y6NwRra2_"
   },
   "source": [
    "Load the motion files into `TimeSeriesTable`s. Print the column labels to check the files have been processed properly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "GeZ65ePyra3A"
   },
   "outputs": [],
   "source": [
    "# Load the normal gait motion file into a TimeSeriesTable.\n",
    "table_normal_gait = osim.TimeSeriesTable('normal_gait.mot')\n",
    "# Print labels for each column.\n",
    "print(table_normal_gait.getColumnLabels())\n",
    "\n",
    "# Load the crouch gait motion file into a TimeSeriesTable.\n",
    "table_crouch_gait = osim.TimeSeriesTable('crouch_gait.mot')\n",
    "# Print labels for each column.\n",
    "print(table_crouch_gait.getColumnLabels())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lzugVUla-leG"
   },
   "source": [
    "Let's obtain the information we want to plot. This time, we obtain two time columns (independent columns), one per each table. Since both have a range from 0 to 1, we can plot them and compare them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "38URdfs9-n-j"
   },
   "outputs": [],
   "source": [
    "# Get columns we want to represent.\n",
    "normal_gait_knee_angle_r = table_normal_gait.getDependentColumn('knee_angle_r')\n",
    "crouch_gait_knee_angle_r = table_crouch_gait.getDependentColumn('knee_angle_r')\n",
    "\n",
    "# Get independent columns of each table (time).\n",
    "normal_gait_time = table_normal_gait.getIndependentColumn()\n",
    "crouch_gait_time = table_crouch_gait.getIndependentColumn()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8sT3M3Ew-dwa"
   },
   "source": [
    "Now let's plot both data series to quantitatively compare."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "oaDrdUA5uCC6"
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# Create six subplots, with 2 rows and 3 columns.\n",
    "fig, axs = plt.subplots(1, 1, figsize=(7, 5))\n",
    "fig.suptitle('Normal Gait vs. Crouch Gait')\n",
    "\n",
    "# Plot the knee angles on the first subplot.\n",
    "axs.plot(normal_gait_time, normal_gait_knee_angle_r.to_numpy(), label='normal_gait_knee_angle_r')\n",
    "axs.plot(crouch_gait_time, crouch_gait_knee_angle_r.to_numpy(), label='crouch_gait_knee_angle_l')\n",
    "axs.set_title('Knee angle')\n",
    "axs.set_xlabel('Time')\n",
    "axs.set_ylabel('Knee angle')\n",
    "axs.grid()\n",
    "axs.legend()\n",
    "\n",
    "# Set the spacing between subplots\n",
    "plt.subplots_adjust(wspace=0.5, hspace=0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "HU5PSGHT5W1P"
   },
   "source": [
    "**Questions: Range of Motion**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "CrxnVBkr5lSk"
   },
   "source": [
    "\n",
    "**1.** Label the times at which heel strike and toe-off occur, and the stance and swing intervals. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "oCaTndxk5nQv"
   },
   "source": [
    "\n",
    "**2.** What is the range of motion for knee flexion during the stance phase for normal gait?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "WHJUGwtL5rKR"
   },
   "source": [
    "**3.** How does the knee flexion range of motion for crouch gait compare to that of normal gait?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ovoeaVcu95-J"
   },
   "source": [
    "### 4.5.2. Hamstrings Length"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VfBhbJtr9E1G"
   },
   "source": [
    "An orthopedic surgeon believes that a patient may benefit from a hamstring lengthening surgery. You are tasked to compare the hamstrings (semitendinosus) length over a patient's crouch gait cycle to the hamstrings length for a \"normal\" gait cycle. This time, a `MuscleAnalysis` must be performed to obtain the fiber lengths of the muscles."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "osLz4Dbv-3PK"
   },
   "source": [
    "First, let's load the motion files to obtain the initial and ending time. In both files, the initial and ending times are the same, so we obtain the values from just one of them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ktoXE8561QRJ"
   },
   "outputs": [],
   "source": [
    "# Load motion file and extract time.\n",
    "tableTime = osim.TimeSeriesTable('normal_gait.mot')\n",
    "x_time = tableTime.getIndependentColumn()\n",
    "\n",
    "# Get first and last time.\n",
    "first_time = x_time[0]\n",
    "last_time = x_time[len(x_time) - 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "91ktx1Ag3OY_"
   },
   "source": [
    "Now, let's create a `MuscleAnalysis` and configure it. This analysis will be applied two times: one for the normal gait and another for the crouch gait."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "dDKQQH5R3T5y"
   },
   "outputs": [],
   "source": [
    "# Define a MuscleAnalysis.\n",
    "muscle_analysis = osim.MuscleAnalysis()\n",
    "\n",
    "# Set start and end times for the analysis.\n",
    "muscle_analysis.setStartTime(first_time)\n",
    "muscle_analysis.setEndTime(last_time)\n",
    "\n",
    "# Set the muscle of interest (semitendinosus on the right leg).\n",
    "muscle_list = osim.ArrayStr()\n",
    "muscle_list.append(\"semiten_r\")\n",
    "muscle_analysis.setMuscles(muscle_list)\n",
    "\n",
    "# Configure the analysis.\n",
    "muscle_analysis.setOn(True)\n",
    "muscle_analysis.setStepInterval(1)\n",
    "muscle_analysis.setInDegrees(True)\n",
    "muscle_analysis.setComputeMoments(True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ABbHd8br4JsA"
   },
   "source": [
    "For the next step, we need to create an `AnalyzeTool` that, given a model file and a motion file, to perform the requested analysis. We have to create an `AnalyzeTool` for the normal gait and another one for the crouch gait. This may take a few seconds."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "AvG1N-t64WKs"
   },
   "outputs": [],
   "source": [
    "## Normal Gait.\n",
    "\n",
    "# Create an AnalyzeTool for normal gait.\n",
    "analyze_tool_normal_gait = osim.AnalyzeTool()\n",
    "analyze_tool_normal_gait.setName(\"Muscle_Analysis_Normal_Gait\")\n",
    "\n",
    "# Set the model file and motion file to analyze.\n",
    "analyze_tool_normal_gait.setModelFilename(\"gait2392.osim\")\n",
    "analyze_tool_normal_gait.setCoordinatesFileName(\"normal_gait.mot\")\n",
    "\n",
    "# Add the MuscleAnalysis to the AnalyzeTool.\n",
    "analyze_tool_normal_gait.updAnalysisSet().cloneAndAppend(muscle_analysis)\n",
    "\n",
    "# Directory where results are stored.\n",
    "analyze_tool_normal_gait.setResultsDir(\"MA_Normal_Gait_Results\")\n",
    "\n",
    "# Configure AnalyzeTool.\n",
    "analyze_tool_normal_gait.setReplaceForceSet(False)\n",
    "analyze_tool_normal_gait.setSolveForEquilibrium(True)\n",
    "analyze_tool_normal_gait.setStartTime(first_time)\n",
    "analyze_tool_normal_gait.setFinalTime(last_time)\n",
    "\n",
    "# Print configuration of the AnalyzeTool to an XML file.\n",
    "analyze_tool_normal_gait.printToXML(\"Muscle_Analysis_Normal_Gait_AnalyzeTool_setup.xml\")\n",
    "\n",
    "# Load configuration and run the analyses. \n",
    "analyze_tool_normal_gait = osim.AnalyzeTool(\"Muscle_Analysis_Normal_Gait_AnalyzeTool_setup.xml\", True)\n",
    "result_normal_gait = analyze_tool_normal_gait.run()\n",
    "\n",
    "\n",
    "## Crouch Gait.\n",
    "\n",
    "# Create an AnalyzeTool for crouch gait.\n",
    "analyze_tool_crouch_gait = osim.AnalyzeTool()\n",
    "analyze_tool_crouch_gait.setName(\"Muscle_Analysis_Crouch_Gait\")\n",
    "\n",
    "# Set the model file and motion file to analyze.\n",
    "analyze_tool_crouch_gait.setModelFilename(\"gait2392.osim\")\n",
    "analyze_tool_crouch_gait.setCoordinatesFileName(\"crouch_gait.mot\")\n",
    "\n",
    "# Add the Muscleanalysis to the AnalyzeTool.\n",
    "analyze_tool_crouch_gait.updAnalysisSet().cloneAndAppend(muscle_analysis)\n",
    "\n",
    "# Directory where results are stored.\n",
    "analyze_tool_crouch_gait.setResultsDir(\"MA_Crouch_Gait_Results\")\n",
    "\n",
    "# Configure AnalyzeTool.\n",
    "analyze_tool_crouch_gait.setReplaceForceSet(False)\n",
    "analyze_tool_crouch_gait.setSolveForEquilibrium(True)\n",
    "analyze_tool_crouch_gait.setStartTime(first_time)\n",
    "analyze_tool_crouch_gait.setFinalTime(last_time)\n",
    "\n",
    "# Print configuration of the AnalyzeTool to an XML file.\n",
    "analyze_tool_crouch_gait.printToXML(\"Muscle_Analysis_Crouch_Gait_AnalyzeTool_setup.xml\")\n",
    "\n",
    "# Load configuration and run the analyses. \n",
    "analyze_tool_crouch_gait = osim.AnalyzeTool(\"Muscle_Analysis_Crouch_Gait_AnalyzeTool_setup.xml\", True)\n",
    "result_crouch_gait = analyze_tool_crouch_gait.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yikNNh5i5WNa"
   },
   "source": [
    "Now that the analysis has been performed, you can extract the fiber length of the semitendinous muscle!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "F5UYk24r5cy7"
   },
   "outputs": [],
   "source": [
    "## Normal gait\n",
    "\n",
    "# Get results from file.\n",
    "table_fiber_length_normal_gait = osim.TimeSeriesTable(\n",
    "    \"MA_Normal_Gait_Results/Muscle_Analysis_Normal_Gait_MuscleAnalysis_FiberLength.sto\")\n",
    "\n",
    "# Get values.\n",
    "time_normal_gait = table_fiber_length_normal_gait.getIndependentColumn()\n",
    "fiber_length_semitendinous_normal_gait = table_fiber_length_normal_gait.getDependentColumn(\"semiten_r\")\n",
    "\n",
    "\n",
    "## Crouch gait\n",
    "\n",
    "# Get results from file.\n",
    "table_fiber_length_crouch_gait = osim.TimeSeriesTable(\n",
    "    \"MA_Crouch_Gait_Results/Muscle_Analysis_Crouch_Gait_MuscleAnalysis_FiberLength.sto\")\n",
    "\n",
    "# Get values.\n",
    "time_crouch_gait = table_fiber_length_crouch_gait.getIndependentColumn()\n",
    "fiber_length_semitendinous_crouch_gait = table_fiber_length_crouch_gait.getDependentColumn(\"semiten_r\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9Sw39wFy6_lZ"
   },
   "source": [
    "Let's create a plot to analyze the fiber length of the semitendinous muscle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "goCMxbavur78"
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# Create six subplots, with 2 rows and 3 columns.\n",
    "fig, axs = plt.subplots(1, 1, figsize=(7, 5))\n",
    "fig.suptitle('Normal Gait vs. Crouch Gait')\n",
    "\n",
    "# Plot the knee angles on the first subplot.\n",
    "axs.plot(time_normal_gait, fiber_length_semitendinous_normal_gait.to_numpy(), label='normal_gait_semitendinous_fiber_length')\n",
    "axs.plot(time_crouch_gait, fiber_length_semitendinous_crouch_gait.to_numpy(), label='crouch_gait_semitendinous_fiber_length')\n",
    "axs.set_title('Fiber Length')\n",
    "axs.set_xlabel('Time')\n",
    "axs.set_ylabel('Fiber Length')\n",
    "axs.grid()\n",
    "axs.legend()\n",
    "\n",
    "# Set the spacing between subplots\n",
    "plt.subplots_adjust(wspace=0.5, hspace=0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "x4L_1mUV50_S"
   },
   "source": [
    "**Questions: Hamstrings Length**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "CFKPhBCN56ny"
   },
   "source": [
    "**1.** Study the curves. Based on the plot, how do the peak hamstring lengths in normal and crouch gait compare? For this patient, would you recommend a hamstrings lengthening surgery? "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fCAAlC3i-E5J"
   },
   "source": [
    "**2.** What are some limitations of your analysis?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "i7rhPT2id6gg"
   },
   "source": [
    "## 4.6. Conclusion"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1XK9UgItWQMF"
   },
   "source": [
    "In this tutorial, you have downloaded and explored a model of the lower extremity. Then, you have used this model to study lower limb kinematics using motion files from previous simulation results. Finally, you have performed a `MuscleAnalysis` to determine the fiber length of the semitendinous muscle to analyze possible underlying causes of crouch gait."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lLWgd8Noun6Y"
   },
   "source": [
    "## 4.7. Useful Links\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "m8M3dlT5xQcD"
   },
   "source": [
    "> **OpenSim Website:** https://opensim.stanford.edu/\n",
    ">\n",
    "> **OpenSim Python Scripting:** https://opensimconfluence.atlassian.net/wiki/spaces/OpenSim/pages/53085346/Scripting+in+Python\n",
    ">\n",
    "> **OpenSim API Documentation:** https://simtk.org/api_docs/opensim/api_docs/\n",
    "> \n",
    "> **OpenSim Creator Website:** https://opensimcreator.com/\n",
    "> \n",
    "> **SimTK Website:** https://simtk.org/projects/opensim\n",
    "> \n",
    "> **Biomechanics of Movement Course Videos:** https://www.youtube.com/channel/UCDNGy0KKNLQ-ztcL5h2Z6zA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rFR2Rf9tk_9A"
   },
   "source": [
    "## 4.8 Acknowledgments\n",
    "\n",
    "Allison Arnold and Scott Delp with help from many others.\n",
    "\n",
    "Thanks to [OpenSimColab](https://simtk.org/projects/opencolab) project [[5]](https://doi.org/10.1080/10255842.2022.2104607) for creating the first OpenSim Conda package."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2rwqw1LFq6Lz"
   },
   "source": [
    "## 4.9. References"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TpbyAlHvfh8M"
   },
   "source": [
    "> [1].   Uchida, T. K., Delp, S. L., & Delp, D. (2021). **Biomechanics of movement: The science of sports, robotics, and rehabilitation.** *MIT Press*. https://mitpress.mit.edu/9780262044202/biomechanics-of-movement/\n",
    ">\n",
    "> [2].   Delp, S. L., Anderson, F. C., Arnold, A. S., Loan, P., Habib, A., John, C. T., Guendelman, E., & Thelen, D. G. (2007). **OpenSim: open-source software to create and analyze dynamic simulations of movement.** *IEEE Transactions on Bio-Medical Engineering*, 54(11), 1940–1950. https://doi.org/10.1109/TBME.2007.901024\n",
    ">\n",
    "> [3].   Delp, S. L., Loan, J. P., Hoy, M. G., Zajac, F. E., Topp, E. L., & Rosen, J. M. (1990). **An interactive graphics-based model of the lower extremity to study orthopaedic surgical procedures.** *IEEE Transactions on Bio-Medical Engineering*, 37(8), 757–767. https://doi.org/10.1109/10.102791\n",
    "> \n",
    "> [4].   Arnold, A. S., Liu, M. Q., Schwartz, M. H., Ounpuu, S., & Delp, S. L. (2006). **The role of estimating muscle-tendon lengths and velocities of the hamstrings in the evaluation and treatment of crouch gait.** *Gait & Posture*, 23(3), 273–281. https://doi.org/10.1016/j.gaitpost.2005.03.003\n",
    ">\n",
    "> [5] Mokhtarzadeh, H., Jiang, F., Zhao, S., & Malekipour, F. (2022). **OpenColab project: OpenSim in Google colaboratory to explore biomechanics on the web.** *Computer Methods in Biomechanics and Biomedical Engineering*, 1–9. https://doi.org/10.1080/10255842.2022.2104607"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "opensim",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
